{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Compaire.ipynb",
      "provenance": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/BenYavor/Comparison_of_communication_Autoencoder_MI_GAN_WGAN/blob/master/Compaire.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Y3Mvn1V30ejH",
        "colab_type": "code",
        "outputId": "2247e8f0-018d-4d26-8f69-c8c621aa0e6d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 644
        }
      },
      "source": [
        "!pip install tensorflow==2.0.0\n",
        "import numpy as np\n",
        "%matplotlib inline\n",
        "import matplotlib.pyplot as plt   \n",
        "import warnings\n",
        "with warnings.catch_warnings():\n",
        "    warnings.filterwarnings(\"ignore\",category=FutureWarning)\n",
        "    import tensorflow as tf\n",
        "import os\n",
        "tf.__version__\n",
        "from tensorflow import keras\n",
        "import time\n",
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "import pandas as pd\n",
        "import sys\n",
        "assert sys.version_info >= (3, 5)\n",
        "%matplotlib inline\n",
        "import matplotlib as mpl\n",
        "mpl.rc('axes', labelsize=14)\n",
        "mpl.rc('xtick', labelsize=12)\n",
        "mpl.rc('ytick', labelsize=12)\n",
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "from sklearn.preprocessing import OneHotEncoder\n",
        "import pandas as pd\n",
        "from scipy import special\n",
        "#from Clustering_Equalgrps.equal_groups import EqualGroupsKMeans\n",
        "from tensorflow.keras import layers\n",
        "np.random.seed(42)\n",
        "tf.random.set_seed(42)"
      ],
      "execution_count": 1,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Requirement already satisfied: tensorflow==2.0.0 in /usr/local/lib/python3.6/dist-packages (2.0.0)\n",
            "Requirement already satisfied: keras-applications>=1.0.8 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.0.8)\n",
            "Requirement already satisfied: google-pasta>=0.1.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.1.8)\n",
            "Requirement already satisfied: absl-py>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.8.1)\n",
            "Requirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.1.0)\n",
            "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.33.6)\n",
            "Requirement already satisfied: tensorboard<2.1.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (2.0.1)\n",
            "Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.1.0)\n",
            "Requirement already satisfied: wrapt>=1.11.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.11.2)\n",
            "Requirement already satisfied: gast==0.2.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.2.2)\n",
            "Requirement already satisfied: opt-einsum>=2.3.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (3.1.0)\n",
            "Requirement already satisfied: protobuf>=3.6.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (3.10.0)\n",
            "Requirement already satisfied: numpy<2.0,>=1.16.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.17.4)\n",
            "Requirement already satisfied: tensorflow-estimator<2.1.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (2.0.1)\n",
            "Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (0.8.0)\n",
            "Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.15.0)\n",
            "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.0.0) (1.12.0)\n",
            "Requirement already satisfied: h5py in /usr/local/lib/python3.6/dist-packages (from keras-applications>=1.0.8->tensorflow==2.0.0) (2.8.0)\n",
            "Requirement already satisfied: google-auth<2,>=1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (1.7.1)\n",
            "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.4.1)\n",
            "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.16.0)\n",
            "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (41.4.0)\n",
            "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.1.1)\n",
            "Requirement already satisfied: cachetools<3.2,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.1.1)\n",
            "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.2.7)\n",
            "Requirement already satisfied: rsa<4.1,>=3.1.4 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (4.0)\n",
            "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (1.3.0)\n",
            "Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /usr/local/lib/python3.6/dist-packages (from pyasn1-modules>=0.2.1->google-auth<2,>=1.6.3->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (0.4.7)\n",
            "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.6/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.1.0)\n",
            "Requirement already satisfied: requests>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (2.21.0)\n",
            "Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests>=2.0.0->requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (2.8)\n",
            "Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests>=2.0.0->requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (3.0.4)\n",
            "Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests>=2.0.0->requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (1.24.3)\n",
            "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests>=2.0.0->requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.1.0,>=2.0.0->tensorflow==2.0.0) (2019.9.11)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1wlZswcMF7Rt",
        "colab_type": "text"
      },
      "source": [
        "#### Vergleich\n",
        "Als erstes für feste $k$ und $n$, was sich ändert ist die Samplesize, Anzahl der Samples und SNR"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4qpY-gawAf-9",
        "colab_type": "text"
      },
      "source": [
        "###Systemparameter\n",
        "ACHTUNG: CHANNELANZAHL WURDE UNTERSCHIEDLICH VERWENDET \\\\\n",
        "$k$ - die Anzhal der bits \\\\\n",
        "$M$ - Anzahl der unterschiedlichen Nachrichten \\\\\n",
        "$n$ - channel uses\\\\\n",
        "$N$ - Länge des Rauschvektors"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "czeNNfpY1qc2",
        "outputId": "5272788c-55a8-42bc-9f3f-eed3bf868d9c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "k = 4      # Number of information bits per message, i.e., M=2**k\n",
        "M = 2**k\n",
        "n = 2    # Number of real channel uses per message\n",
        "#k = int(np.log2(M))\n",
        "#n = 2\n",
        "print(M)\n",
        "\n",
        "SNR = 7\n"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "16\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BA4TqJBOXXIg",
        "colab_type": "text"
      },
      "source": [
        "## Training Parameter"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "aJZ_cnp9V-Q6",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "gen_learning_rate=0.0001 \n",
        "disc_learning_rate = 0.0005                  # 0.0001  "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tb-DiBwSN255",
        "colab_type": "text"
      },
      "source": [
        "### Different Layers"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hFMMLrY0LthL",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "randN_initial = keras.initializers.RandomNormal(mean=0.0, stddev=0.05, seed=None)\n",
        "\n",
        "EncIn = tf.keras.layers.Input(shape=(M,))#, dtype= tf.int32)\n",
        "e1 = tf.keras.layers.Dense(2*n, activation=None)\n",
        "e2 = tf.keras.layers.Lambda(lambda x:tf.reshape(x, shape=[-1,int(n/2),2]))\n",
        "EncOut = tf.keras.layers.Lambda(lambda x: x/tf.sqrt(2*tf.reduce_mean(tf.square(x))))\n",
        "GenIn = tf.keras.layers.Lambda(lambda x:tf.reshape(x,(tf.shape(x)[0],-1)))\n",
        "# = tf.keras.layers.Lambda(generator)\n",
        "DecIn = tf.keras.layers.Lambda(lambda x:tf.reshape(x, shape=[-1,int(n/2),2]))\n",
        "d1 = tf.keras.layers.Lambda(lambda x:tf.reshape(x, shape=[-1,n]))\n",
        "d2 = tf.keras.layers.Dense(M, activation='relu')\n",
        "DecOut = tf.keras.layers.Dense(M, activation='softmax')\n",
        "\n",
        "\n",
        "#noise_std = EbNo_to_noise(TRAINING_SNR)\n",
        "# custom functions / layers without weights\n",
        "norm_layer = keras.layers.Lambda(lambda x: tf.divide(x,tf.sqrt(2*tf.reduce_mean(tf.square(x)))))\n",
        "shape_layer = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,2,n]))\n",
        "shape_layer2 = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,n]))\n",
        "channel_layer = keras.layers.Lambda(lambda x: x + tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7J96hJhKO9VJ",
        "colab_type": "text"
      },
      "source": [
        "### Help functions"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uV7pjryDv4M4",
        "colab_type": "code",
        "outputId": "aedbc9de-ffd3-46dd-e315-6788fd02317f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "def EbNo2Sigma(ebnodb):\n",
        "    '''Convert Eb/No in dB to noise standard deviation'''\n",
        "    ebno = 10**(ebnodb/10)\n",
        "    return 1/np.sqrt(2*(2*k/n)*ebno)\n",
        "\n",
        "def EbNo_to_noise(ebnodb):\n",
        "    '''Transform EbNo[dB]/snr to noise power'''\n",
        "    ebno = 10**(ebnodb/10)\n",
        "    noise_std = 1/np.sqrt(2*(2*k/n)*ebno) \n",
        "    return noise_std\n",
        "\n",
        "\n",
        "def real_channel(x,noise_std):\n",
        "    # Black-box Channel\n",
        "    #AWGN\n",
        "    return x + tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std)\n",
        "\n",
        "    #Rayleigh\n",
        "    #return x + tf.sqrt(tf.square(tf.random_normal(tf.shape(x), mean=0.0, stddev=noise_std)) + tf.square(tf.random_normal(tf.shape(x), mean=0.0, stddev=noise_std)))\n",
        "    \n",
        "    #Uniform U(-3;3)    \n",
        "    #return x + tf.random_uniform(tf.shape(x), minval=-2, maxval=2)\n",
        "\n",
        "def B_Ber(input_msg, msg):\n",
        "    '''Calculate the Batch Bit Error Rate'''\n",
        "    pred_error = tf.not_equal(tf.argmax(msg, 1), tf.argmax(input_msg, 1))\n",
        "    bber = tf.reduce_mean(tf.cast(pred_error, tf.float32))\n",
        "    return bber\n",
        "\n",
        "def random_sample(batch_size=32):\n",
        "    msg = np.random.randint(M, size=batch_size)\n",
        "    return msg\n",
        "\n",
        "\n",
        "\n",
        "\n",
        "def B_Ber_m(input_msg, msg):\n",
        "    '''Calculate the Batch Bit Error Rate'''\n",
        "    pred_error = tf.not_equal(input_msg, tf.argmax(msg, 1))      \n",
        "    bber = tf.reduce_mean(tf.cast(pred_error, tf.float32))\n",
        "    return bber\n",
        "\n",
        "def SNR_to_noise(snrdb):\n",
        "    '''Transform EbNo[dB]/snr to noise power'''\n",
        "    snr = 10**(snrdb/10)\n",
        "    noise_std = 1/np.sqrt(2*snr)\n",
        "    return noise_std\n",
        "\n",
        "noise_std = EbNo2Sigma(SNR)\n",
        "\n",
        "print(EbNo2Sigma(SNR))\n",
        "print(EbNo_to_noise(SNR))"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "0.15792649852735607\n",
            "0.15792649852735607\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "OBGMgdDEh7uX",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def test_encoding(M=16, n=1):\n",
        "    inp = np.arange(0,M)\n",
        "    coding = gan_encoder.predict(inp)\n",
        "    fig = plt.figure(figsize=(4,4))\n",
        "    plt.plot(coding[:,0], coding[:, 1], \"b.\")\n",
        "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
        "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
        "    plt.grid(True)\n",
        "    plt.gca().set_ylim(-2, 2)\n",
        "    plt.gca().set_xlim(-2, 2)\n",
        "    plt.show()\n",
        "\n",
        "def test_w_encoding(M=16, n=1):\n",
        "    inp = np.arange(0,M)\n",
        "    coding = w_gan_encoder.predict(inp)\n",
        "    fig = plt.figure(figsize=(4,4))\n",
        "    plt.plot(coding[:,0], coding[:, 1], \"b.\")\n",
        "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
        "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
        "    plt.grid(True)\n",
        "    plt.gca().set_ylim(-2, 2)\n",
        "    plt.gca().set_xlim(-2, 2)\n",
        "    plt.show()\n",
        "\n",
        "def test_noisy_codeword(data):\n",
        "    rcvd_word = data[1:2000]\n",
        "    fig = plt.figure(figsize=(4,4))\n",
        "    plt.plot(rcvd_word[:,0], rcvd_word[:, 1], \"b.\")\n",
        "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
        "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
        "    plt.grid(True)\n",
        "    plt.gca().set_ylim(-2, 2)\n",
        "    plt.gca().set_xlim(-2, 2)\n",
        "    plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AOoYuK_jR9rH",
        "colab_type": "text"
      },
      "source": [
        "# Models"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PQxhmgOa0_7c",
        "colab_type": "text"
      },
      "source": [
        "#### Generator Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LXbS5lM9Tb9B",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_generator(n):\n",
        "  input1 = tf.keras.layers.Input(shape=(n,))\n",
        "  x1 = tf.keras.layers.Dense(n)(input1)\n",
        "  input2 =tf.random.normal(shape=tf.shape(input1))\n",
        "  #input2 =tf.random.normal([tf.shape(input1)[0],n])\n",
        "  x2 = tf.keras.layers.Dense(n)(input2)\n",
        "  subtracted = tf.keras.layers.Concatenate(1)([x1, x2])\n",
        "  h1 = tf.keras.layers.Dense(32,use_bias=True,  activation='relu')(subtracted)\n",
        "  h2 = tf.keras.layers.Dense(32,use_bias=True, activation='relu')(h1)\n",
        "  out = tf.keras.layers.Dense(n, use_bias= True, activation='linear')(h2)\n",
        "  generator = tf.keras.models.Model(inputs=[input1], outputs=out)\n",
        "  return generator\n",
        "\n",
        "def get_w_generator(n):\n",
        "  inputx1 = tf.keras.layers.Input(shape=(n,))\n",
        "  x1 = tf.keras.layers.Dense(n)(inputx1)\n",
        "  inputx2 =tf.random.normal(shape=tf.shape(inputx1))\n",
        "  x2 = tf.keras.layers.Dense(n)(inputx2)\n",
        "  subtracted = tf.keras.layers.Concatenate(1)([x1, x2])\n",
        "  h1 = tf.keras.layers.Dense(32,use_bias=True,  activation='relu')(subtracted)\n",
        "  h2 = tf.keras.layers.Dense(32,use_bias=True, activation='relu')(h1)\n",
        "  out = tf.keras.layers.Dense(n, use_bias= True, activation='linear')(h2)\n",
        "  generator = tf.keras.models.Model(inputs=[inputx1], outputs=out)\n",
        "  return generator\n",
        "#keras.utils.plot_model(generator, 'Structure_of_MI_estimation.png', show_shapes=True)\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Vt2rTP7hSFt4",
        "colab_type": "text"
      },
      "source": [
        "#### Discriminator Model "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "97h2eMLeXS68",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_discriminator(n):\n",
        "  model = tf.keras.Sequential()\n",
        "  model.add(tf.keras.layers.Dense(32,use_bias=True, kernel_initializer=randN_initial,activation='relu',input_shape=((2*n,))))\n",
        "  #model.add(tf.keras.layers.Dense(32,use_bias=True, kernel_initializer=randN_initial, activation='relu'))\n",
        "  model.add(tf.keras.layers.Dense(1,use_bias=False, activation='sigmoid'))\n",
        "  return model\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6rIdAYRhHgGk",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "w_generator = get_w_generator(n)\n",
        "w_discriminator = get_discriminator(n)\n",
        "generator = get_generator(n)\n",
        "discriminator = get_discriminator(n)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lcIzLZj5Seh9",
        "colab_type": "text"
      },
      "source": [
        "#### Encoder GAN Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sNHtzAC4SPBq",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_gan_encoder(M):\n",
        "  model = keras.models.Sequential([\n",
        "            keras.layers.Embedding(M, M, embeddings_initializer='glorot_normal'),\n",
        "            keras.layers.Dense(M, activation=\"elu\"),\n",
        "            keras.layers.Dense(n, activation=None),\n",
        "            e2,\n",
        "            EncOut,\n",
        "            GenIn])\n",
        "  return model\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "G5iCDE4dSL35",
        "colab_type": "text"
      },
      "source": [
        "#### decoder GAN Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "C5KjEhDvSWQR",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_gan_decoder(M):\n",
        "   model= keras.models.Sequential([\n",
        "                #DecIn,\n",
        "                #d1,\n",
        "                keras.layers.Dense(M, activation=\"elu\"),\n",
        "                keras.layers.Dense(M, activation=\"softmax\")\n",
        "                ])\n",
        "   return model"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s-t76sYBtlP1",
        "colab_type": "text"
      },
      "source": [
        "### MI estimator Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jL9PYSKjIoOI",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "randN_05 = keras.initializers.RandomNormal(mean=0.0, stddev=0.05, seed=None)\n",
        "bias_init = keras.initializers.Constant(0.01)\n",
        "\n",
        "input_A = keras.layers.Input(shape=[2*n])\n",
        "input_B = keras.layers.Input(shape=[2*n])\n",
        "    \n",
        "transform = keras.models.Sequential([\n",
        "layers.Dense(256, bias_initializer=bias_init, kernel_initializer=randN_05, activation=\"relu\"),\n",
        "#keras.layers.Dropout(rate=0.3), # To regularize higher dimensionality\n",
        "layers.Dense(256, bias_initializer=bias_init, kernel_initializer=randN_05, activation=\"relu\"),\n",
        "#keras.layers.Dropout(rate=0.3), # To regularize higher dimensionality\n",
        "layers.Dense(1, bias_initializer=bias_init, kernel_initializer=randN_05, activation=None)])\n",
        "\n",
        "output_A = transform(input_A)\n",
        "output_B = transform(input_B)\n",
        "output_C = tf.reduce_mean(output_A) - tf.math.log(tf.reduce_mean(tf.exp(output_B))) # MINE\n",
        "MI_mod = keras.models.Model(inputs=[input_A, input_B], outputs=output_C)\n",
        "\n",
        "#keras.utils.plot_model(MI_mod, 'Structure_of_MI_estimation.png', show_shapes=True)\n",
        "\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cf_rmCXIt6af",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "mi_encoder = keras.models.Sequential([\n",
        "            keras.layers.Embedding(M, M, embeddings_initializer='glorot_normal'),\n",
        "            keras.layers.Dense(M, activation=\"elu\"),\n",
        "            keras.layers.Dense(n, activation=None),\n",
        "            shape_layer,\n",
        "            norm_layer])\n",
        "\n",
        "mi_channel = keras.models.Sequential([channel_layer])\n",
        "\n",
        "mi_decoder = keras.models.Sequential([\n",
        "                keras.layers.InputLayer(input_shape=[2,int(n/2)]),\n",
        "                shape_layer2,\n",
        "                keras.layers.Dense(M, activation=\"elu\"),\n",
        "                keras.layers.Dense(M, activation=\"softmax\")\n",
        "                ])\n",
        "\n",
        "autoencoder = keras.models.Sequential([mi_encoder, mi_channel, mi_decoder])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "n15AvPFO05gd",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def gan_optimizers(gen_learning_rate  ,disc_learning_rate):\n",
        "  generator_optimizer = tf.keras.optimizers.RMSprop(gen_learning_rate)      #RMSprop   in oreder to test where the error comes from\n",
        "  discriminator_optimizer = tf.keras.optimizers.RMSprop(disc_learning_rate) \n",
        "  return generator_optimizer, discriminator_optimizer"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "U4cPlvbWIoO5",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def plot_loss(step, epoch, mean_loss, X_batch, y_pred, plot_encoding):\n",
        "    template = 'Iteration: {}, Epoch: {}, Loss: {:.5f}, Batch_BER: {:.5f}'\n",
        "    if step % 10 == 0:\n",
        "        print(template.format(step, epoch, mean_loss.result(), B_Ber_m(X_batch, y_pred)))\n",
        "        if plot_encoding:\n",
        "            test_encoding()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "r-0gFRfXIoPB",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def plot_batch_loss(epoch, mean_loss, X_batch, y_pred):\n",
        "        template_outer_loop = 'Interim result for Epoch: {}, Loss: {:.5f}, Batch_BER: {:.5f}'\n",
        "        print(template_outer_loop.format(epoch, mean_loss.result(), B_Ber_m(X_batch, y_pred)))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "FcXv9vthIoPG",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def split_train_data(x_sample, y_sample):\n",
        "    x_shaped = tf.reshape(x_sample, shape=[-1,n])\n",
        "    y_shaped = tf.reshape(y_sample, shape=[-1,n])\n",
        "    x_sample1, x_sample2 = tf.split(x_shaped, num_or_size_splits=2)\n",
        "    y_sample1, y_sample2 = tf.split(y_shaped, num_or_size_splits=2)\n",
        "    joint_sample = tf.concat([x_sample1, y_sample1], axis=1)\n",
        "    marg_sample = tf.concat([x_sample2, y_sample1], axis=1)\n",
        "    return joint_sample, marg_sample"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "B6NAzlIiIoPM",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "\n",
        "def train_mi(n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.005):\n",
        "    optimizer_mi = keras.optimizers.Nadam(lr=learning_rate)\n",
        "    for epoch in range(1, n_epochs + 1): \n",
        "        print(\"Training in Epoch {}/{}\".format(epoch, n_epochs)) \n",
        "        for step in range(1, n_steps + 1):\n",
        "            X_batch = random_sample(batch_size*2)\n",
        "            with tf.GradientTape() as tape:\n",
        "                x_enc = mi_encoder(X_batch, training=True)\n",
        "                y_recv = mi_channel(x_enc)\n",
        "                joint_marg_s = split_train_data(x_enc, y_recv)\n",
        "                loss = -MI_mod(joint_marg_s)\n",
        "                gradients = tape.gradient(loss, MI_mod.trainable_variables) \n",
        "                optimizer_mi.apply_gradients(zip(gradients, MI_mod.trainable_variables))\n",
        "            mi_avg = -mean_loss(loss)\n",
        "        print('Epoch: {}, Mi is {}'.format(epoch, mi_avg))\n",
        "        mean_loss.reset_states()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "P-4O8w_HIoPS",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "\n",
        "def train_decoder(n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.005, plot_encoding=True):\n",
        "    optimizer_ae = keras.optimizers.Nadam(lr=learning_rate)\n",
        "    for epoch in range(1, n_epochs + 1): \n",
        "        print(\"Training Bob in Epoch {}/{}\".format(epoch, n_epochs)) \n",
        "        for step in range(1, n_steps + 1):\n",
        "            X_batch  = random_sample(batch_size)\n",
        "            with tf.GradientTape() as tape:\n",
        "                y_pred = autoencoder(X_batch, training=True)\n",
        "                loss = tf.reduce_mean(loss_fn(X_batch, y_pred))\n",
        "                gradients = tape.gradient(loss, mi_decoder.trainable_variables) \n",
        "                optimizer_ae.apply_gradients(zip(gradients, mi_decoder.trainable_variables)) \n",
        "            mean_loss(loss)\n",
        "            plot_loss(step, epoch, mean_loss, X_batch, y_pred, plot_encoding)\n",
        "        plot_batch_loss(epoch, mean_loss, X_batch, y_pred) \n",
        "        mean_loss.reset_states()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "MDCIupPhIoPY",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "\n",
        "def train_encoder(n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.05):\n",
        "    optimizer_mi = keras.optimizers.Nadam(lr=0.005)\n",
        "    optimizer_ae = keras.optimizers.Nadam(lr=learning_rate)\n",
        "    for epoch in range(1, n_epochs + 1): \n",
        "        print(\"Training Bob in Epoch {}/{}\".format(epoch, n_epochs)) \n",
        "        for step in range(1, n_steps + 1):\n",
        "            X_batch  = random_sample(batch_size)\n",
        "            with tf.GradientTape() as tape:\n",
        "                x_enc = mi_encoder(X_batch, training=True)\n",
        "                y_recv = mi_channel(x_enc)\n",
        "                joint_marg_s = split_train_data(x_enc, y_recv)\n",
        "                loss = -MI_mod(joint_marg_s)\n",
        "                gradients = tape.gradient(loss, mi_encoder.trainable_variables) \n",
        "                optimizer_ae.apply_gradients(zip(gradients, mi_encoder.trainable_variables))\n",
        "            mi_avg = -mean_loss(loss)\n",
        "        with tf.GradientTape() as tape:\n",
        "            X_batch  = random_sample(batch_size) \n",
        "            x_enc = mi_encoder(X_batch, training=True)\n",
        "            y_recv = mi_channel(x_enc)\n",
        "            joint_marg_s = split_train_data(x_enc, y_recv)\n",
        "            loss = -MI_mod(joint_marg_s)\n",
        "            gradients = tape.gradient(loss, MI_mod.trainable_variables) \n",
        "            optimizer_mi.apply_gradients(zip(gradients, MI_mod.trainable_variables))\n",
        "        print('Epoch: {}, Mi is {}'.format(epoch, mi_avg))\n",
        "       # test_encoding(M, 1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ooDukkHvmduJ",
        "colab_type": "code",
        "outputId": "d509dc8f-3d58-4ca2-afcc-3248fac9329d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "%%time\n",
        "def train_gan(epochs,n_steps, batch_size, SNR_level):\n",
        "  noise_std = EbNo2Sigma(SNR_level)\n",
        "  start = time.time()\n",
        "  x = tf.random.normal((batch_size,n),dtype=tf.dtypes.float32) \n",
        "  x = x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  counter = 0\n",
        "  epoch = 0\n",
        "  for epoch in range(epochs):\n",
        "    counter += 1\n",
        "    train_step(noise_std, n_steps,batch_size)\n",
        "    if counter%100==0:\n",
        "      tf.print(\"counter %d:\" % (counter))\n",
        "      fake_c = generator(x)\n",
        "      tf.print(fake_c[0])\n",
        "    #print ('Time for epoch {} is {} sec,'.format(epoch + 1, time.time()-start))\n",
        "      tf.print ('Time for epoch {},'.format(epoch + 1))\n",
        "      \n",
        "  tf.saved_model.save(generator,'/tmp/saved_model/')\n",
        "  tf.print ('Time for the training is {} sec,'.format( time.time()-start))\n",
        "    "
      ],
      "execution_count": 21,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "CPU times: user 3 µs, sys: 0 ns, total: 3 µs\n",
            "Wall time: 7.15 µs\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "WE_JS7kgA1W-",
        "colab": {}
      },
      "source": [
        "@tf.function\n",
        "def train_step(noise_std,n_steps,batch_size):\n",
        "  x = tf.random.normal((batch_size,n),dtype=tf.dtypes.float32) \n",
        "  x = x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  for i in range(n_steps):\n",
        "    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n",
        "      real_training_data = tf.concat(values=[real_channel(x,noise_std), x], axis=1)\n",
        "      fake_training_data = tf.concat(values=[generator(x),x], axis=1)\n",
        "      real_output = discriminator(real_training_data)\n",
        "      fake_output = discriminator(fake_training_data)\n",
        "      \n",
        "      \n",
        "      disc_loss = -tf.reduce_mean(tf.math.log(real_output) + tf.math.log(1. - fake_output))\n",
        "      gen_loss =-tf.reduce_mean(tf.math.log(fake_output))\n",
        "      \n",
        "      #tf.print(disc_loss,gen_loss)\n",
        "      \n",
        "      if tf.math.is_nan(disc_loss) == False:\n",
        "        gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)\n",
        "        discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))\n",
        "    \n",
        "      if i == 4:  \n",
        "        gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n",
        "        generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))\n",
        "\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "y82FQj3Jmvxx",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def creating_and_train_gan(epochs,n_steps, batch_size, SNR_level , n ):  #optional Leraning Rates\n",
        "  train_gan(epochs, n_steps, batch_size, SNR_level)\n",
        "  #4 after GAN training\n",
        "  generator.trainable = False\n",
        "  tf.print(generator.trainable)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dLTtFoV0IoPj",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def mi_Test_AE(data):\n",
        "    '''Calculate Bit Error for varying SNRs'''\n",
        "    snr_range = np.linspace(0, 15, 31)\n",
        "    bber_vec = [None] * len(snr_range)\n",
        "        \n",
        "    for db in range(len(snr_range)):           \n",
        "        noise_std = EbNo_to_noise(snr_range[db])\n",
        "        code_word = mi_encoder(data)\n",
        "        rcvd_word = code_word + tf.random.normal(tf.shape(code_word), mean=0.0, stddev=noise_std)\n",
        "        dcoded_msg = mi_decoder(rcvd_word)\n",
        "        bber_vec[db] = B_Ber_m(data, dcoded_msg)\n",
        "        if (db % 6 == 0) & (db > 0):\n",
        "            print(f'Progress: {db} of {30} parts')\n",
        "\n",
        "    return (snr_range, bber_vec)\n",
        "\n",
        "\n",
        "def gan_Test_AE(data):\n",
        "    '''Calculate Bit Error for varying SNRs'''\n",
        "    snr_range = np.linspace(0, 15, 31)\n",
        "    bber_vec = [None] * len(snr_range)\n",
        "        \n",
        "    for db in range(len(snr_range)):           \n",
        "        noise_std = EbNo_to_noise(snr_range[db])\n",
        "        code_word = gan_encoder(data)\n",
        "        rcvd_word = code_word + tf.random.normal(tf.shape(code_word), mean=0.0, stddev=noise_std)\n",
        "        dcoded_msg = gan_decoder(rcvd_word)\n",
        "        bber_vec[db] = B_Ber_m(data, dcoded_msg)\n",
        "        if (db % 6 == 0) & (db > 0):\n",
        "            print(f'Progress: {db} of {30} parts')\n",
        "\n",
        "    return (snr_range, bber_vec)\n",
        "\n",
        "def w_gan_Test_AE(data):\n",
        "    '''Calculate Bit Error for varying SNRs'''\n",
        "    snr_range = np.linspace(0, 15, 31)\n",
        "    bber_vec = [None] * len(snr_range)\n",
        "        \n",
        "    for db in range(len(snr_range)):           \n",
        "        noise_std = EbNo_to_noise(snr_range[db])\n",
        "        code_word = w_gan_encoder(data)\n",
        "        rcvd_word = code_word + tf.random.normal(tf.shape(code_word), mean=0.0, stddev=noise_std)\n",
        "        dcoded_msg = w_gan_decoder(rcvd_word)\n",
        "        bber_vec[db] = B_Ber_m(data, dcoded_msg)\n",
        "        if (db % 6 == 0) & (db > 0):\n",
        "            print(f'Progress: {db} of {30} parts')\n",
        "\n",
        "    return (snr_range, bber_vec)\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3m0fQ6OXgPf1",
        "colab_type": "text"
      },
      "source": [
        "# GAN Training"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "nNFwwZrcgOkn",
        "colab_type": "code",
        "outputId": "54e0eb14-ef0e-4d00-c342-dde36e3c7b51",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        }
      },
      "source": [
        "%%time\n",
        "\n",
        "generator_optimizer, discriminator_optimizer = gan_optimizers(gen_learning_rate=gen_learning_rate, disc_learning_rate = disc_learning_rate)\n",
        "start = time.time()\n",
        "creating_and_train_gan(epochs= 5000,n_steps=5, batch_size =100, SNR_level = 7, n = n)\n",
        "time_to_train_gan = time.time()-start\n",
        "tf.print ('Time for the training is {} sec,'.format( time.time()-start))"
      ],
      "execution_count": 25,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "counter 100:\n",
            "[-0.0317160524 0.130993053]\n",
            "Time for epoch 100,\n",
            "counter 200:\n",
            "[0.0115681309 0.349815577]\n",
            "Time for epoch 200,\n",
            "counter 300:\n",
            "[-0.0318347849 0.183775589]\n",
            "Time for epoch 300,\n",
            "counter 400:\n",
            "[-0.00576118752 0.288710415]\n",
            "Time for epoch 400,\n",
            "counter 500:\n",
            "[-0.840965748 0.669303536]\n",
            "Time for epoch 500,\n",
            "counter 600:\n",
            "[0.251199901 0.595262408]\n",
            "Time for epoch 600,\n",
            "counter 700:\n",
            "[0.254876494 0.774338365]\n",
            "Time for epoch 700,\n",
            "counter 800:\n",
            "[0.489715427 0.921243]\n",
            "Time for epoch 800,\n",
            "counter 900:\n",
            "[0.617341816 0.774211764]\n",
            "Time for epoch 900,\n",
            "counter 1000:\n",
            "[0.750859 0.828274906]\n",
            "Time for epoch 1000,\n",
            "counter 1100:\n",
            "[0.422623187 0.689234793]\n",
            "Time for epoch 1100,\n",
            "counter 1200:\n",
            "[0.619195282 0.712426543]\n",
            "Time for epoch 1200,\n",
            "counter 1300:\n",
            "[0.598281085 0.614370286]\n",
            "Time for epoch 1300,\n",
            "counter 1400:\n",
            "[0.47829771 0.739305913]\n",
            "Time for epoch 1400,\n",
            "counter 1500:\n",
            "[0.522771895 0.735731244]\n",
            "Time for epoch 1500,\n",
            "counter 1600:\n",
            "[0.827664196 0.828983486]\n",
            "Time for epoch 1600,\n",
            "counter 1700:\n",
            "[0.283022732 0.637997568]\n",
            "Time for epoch 1700,\n",
            "counter 1800:\n",
            "[0.598209 0.713667393]\n",
            "Time for epoch 1800,\n",
            "counter 1900:\n",
            "[0.567686915 0.704547167]\n",
            "Time for epoch 1900,\n",
            "counter 2000:\n",
            "[0.582055509 0.68277]\n",
            "Time for epoch 2000,\n",
            "counter 2100:\n",
            "[0.483814597 0.72983706]\n",
            "Time for epoch 2100,\n",
            "counter 2200:\n",
            "[0.58586 0.650793552]\n",
            "Time for epoch 2200,\n",
            "counter 2300:\n",
            "[0.665031 0.752131462]\n",
            "Time for epoch 2300,\n",
            "counter 2400:\n",
            "[0.355764121 1.02411962]\n",
            "Time for epoch 2400,\n",
            "counter 2500:\n",
            "[0.538052738 0.878064811]\n",
            "Time for epoch 2500,\n",
            "counter 2600:\n",
            "[0.552982807 0.932423174]\n",
            "Time for epoch 2600,\n",
            "counter 2700:\n",
            "[0.550332129 0.68448478]\n",
            "Time for epoch 2700,\n",
            "counter 2800:\n",
            "[0.565040767 0.484878957]\n",
            "Time for epoch 2800,\n",
            "counter 2900:\n",
            "[0.775730312 0.727026343]\n",
            "Time for epoch 2900,\n",
            "counter 3000:\n",
            "[0.141352966 0.961702645]\n",
            "Time for epoch 3000,\n",
            "counter 3100:\n",
            "[0.549330235 0.658917904]\n",
            "Time for epoch 3100,\n",
            "counter 3200:\n",
            "[0.518716 0.768749177]\n",
            "Time for epoch 3200,\n",
            "counter 3300:\n",
            "[0.360731 0.696649075]\n",
            "Time for epoch 3300,\n",
            "counter 3400:\n",
            "[0.560255766 0.771820188]\n",
            "Time for epoch 3400,\n",
            "counter 3500:\n",
            "[0.434400082 0.766544223]\n",
            "Time for epoch 3500,\n",
            "counter 3600:\n",
            "[0.355157584 0.459281325]\n",
            "Time for epoch 3600,\n",
            "counter 3700:\n",
            "[0.313882142 0.671919227]\n",
            "Time for epoch 3700,\n",
            "counter 3800:\n",
            "[0.463566452 0.537470043]\n",
            "Time for epoch 3800,\n",
            "counter 3900:\n",
            "[0.499826282 0.668339729]\n",
            "Time for epoch 3900,\n",
            "counter 4000:\n",
            "[0.305701524 0.565552473]\n",
            "Time for epoch 4000,\n",
            "counter 4100:\n",
            "[0.732834101 0.711431623]\n",
            "Time for epoch 4100,\n",
            "counter 4200:\n",
            "[0.564767599 0.563719511]\n",
            "Time for epoch 4200,\n",
            "counter 4300:\n",
            "[0.571967483 0.65013361]\n",
            "Time for epoch 4300,\n",
            "counter 4400:\n",
            "[0.434883624 0.732791]\n",
            "Time for epoch 4400,\n",
            "counter 4500:\n",
            "[0.31281051 0.754602194]\n",
            "Time for epoch 4500,\n",
            "counter 4600:\n",
            "[0.385274559 0.896041572]\n",
            "Time for epoch 4600,\n",
            "counter 4700:\n",
            "[0.275138825 0.530541]\n",
            "Time for epoch 4700,\n",
            "counter 4800:\n",
            "[0.519933164 0.598569214]\n",
            "Time for epoch 4800,\n",
            "counter 4900:\n",
            "[0.543903232 0.828597546]\n",
            "Time for epoch 4900,\n",
            "counter 5000:\n",
            "[0.552281618 0.430285096]\n",
            "Time for epoch 5000,\n",
            "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\n",
            "Instructions for updating:\n",
            "If using Keras pass *_constraint arguments to layers.\n",
            "INFO:tensorflow:Assets written to: /tmp/saved_model/assets\n",
            "Time for the training is 14.053214073181152 sec,\n",
            "False\n",
            "Time for the training is 14.05681562423706 sec,\n",
            "CPU times: user 19.6 s, sys: 475 ms, total: 20 s\n",
            "Wall time: 14.1 s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6GW2opX7SwMo",
        "colab_type": "text"
      },
      "source": [
        "\n",
        "# AE training"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "FiuN3SZYpeTU",
        "colab_type": "code",
        "outputId": "957b5b2e-e004-44b8-c02c-34a2f5c4e798",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 517
        }
      },
      "source": [
        "%%time\n",
        "\n",
        "gan_decoder = get_gan_decoder(M)\n",
        "gan_encoder = get_gan_encoder(M)\n",
        "\n",
        "\n",
        "\n",
        "gan_AE = tf.keras.models.Sequential([gan_encoder,generator,gan_decoder])\n",
        "data, test_data = random_sample(10000000), random_sample(10000)\n",
        "start = time.time()\n",
        "gan_AE.compile(optimizer=keras.optimizers.Nadam(lr=0.005),loss='sparse_categorical_crossentropy',metrics=['accuracy'])\n",
        "history = gan_AE.fit(data, data, batch_size=500,steps_per_epoch=400, epochs=10)\n",
        "time_to_train_gan += time.time()-start\n",
        "tf.print ('Time for the training is {} sec,'.format( time.time()-start))\n",
        "gan_AE.summary()  "
      ],
      "execution_count": 26,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Train on 10000000 samples\n",
            "Epoch 1/10\n",
            "  199500/10000000 [..............................] - ETA: 2:49 - loss: 0.8786 - accuracy: 0.7928Epoch 2/10\n",
            "  195000/10000000 [..............................] - ETA: 1:15 - loss: 0.2303 - accuracy: 0.9413Epoch 3/10\n",
            "  198500/10000000 [..............................] - ETA: 1:14 - loss: 0.1799 - accuracy: 0.9465Epoch 4/10\n",
            "  193500/10000000 [..............................] - ETA: 1:10 - loss: 0.1659 - accuracy: 0.9493Epoch 5/10\n",
            "  194500/10000000 [..............................] - ETA: 1:11 - loss: 0.1556 - accuracy: 0.9519Epoch 6/10\n",
            "  195000/10000000 [..............................] - ETA: 1:09 - loss: 0.1526 - accuracy: 0.9531Epoch 7/10\n",
            "  199500/10000000 [..............................] - ETA: 1:06 - loss: 0.1460 - accuracy: 0.9548Epoch 8/10\n",
            "  197500/10000000 [..............................] - ETA: 1:17 - loss: 0.1443 - accuracy: 0.9559Epoch 9/10\n",
            "  197000/10000000 [..............................] - ETA: 1:34 - loss: 0.1423 - accuracy: 0.9555Epoch 10/10\n",
            "  194000/10000000 [..............................] - ETA: 1:05 - loss: 0.1379 - accuracy: 0.9569Time for the training is 17.308661699295044 sec,\n",
            "Model: \"sequential_9\"\n",
            "_________________________________________________________________\n",
            "Layer (type)                 Output Shape              Param #   \n",
            "=================================================================\n",
            "sequential_8 (Sequential)    (None, None)              562       \n",
            "_________________________________________________________________\n",
            "model_1 (Model)              (None, 2)                 1294      \n",
            "_________________________________________________________________\n",
            "sequential_7 (Sequential)    (None, 16)                320       \n",
            "=================================================================\n",
            "Total params: 2,176\n",
            "Trainable params: 882\n",
            "Non-trainable params: 1,294\n",
            "_________________________________________________________________\n",
            "CPU times: user 22.4 s, sys: 727 ms, total: 23.1 s\n",
            "Wall time: 17.6 s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ngrucnfWBOHl",
        "colab_type": "text"
      },
      "source": [
        "### Training MI"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CTnDsMN2IoOz",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "loss_fn = keras.losses.SparseCategoricalCrossentropy()\n",
        "mean_loss = keras.metrics.Mean()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "TZTfQcWRIoPn",
        "colab_type": "code",
        "outputId": "5dd293eb-b9cf-49da-bfb1-2e3b15bfba12",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        }
      },
      "source": [
        "%%time\n",
        "start = time.time()\n",
        "train_mi(n_epochs=1, n_steps=500, batch_size=100)\n",
        "train_encoder(n_epochs=5, n_steps=100, batch_size=500, learning_rate=0.005)\n",
        "#test_encoding(M, n)\n",
        "train_encoder(n_epochs=5, n_steps=100, batch_size=500, learning_rate=0.0005)\n",
        "#test_encoding(M, n)\n",
        "train_decoder(n_epochs=5, n_steps=100, batch_size=500, learning_rate=0.005, plot_encoding=False)\n",
        "time_to_train_mi= time.time()-start\n",
        "tf.print ('Time for the training is {} sec,'.format( time.time()-start))"
      ],
      "execution_count": 28,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Training in Epoch 1/1\n",
            "Epoch: 1, Mi is 1.9857680797576904\n",
            "Training Bob in Epoch 1/5\n",
            "Epoch: 1, Mi is 2.4169111251831055\n",
            "Training Bob in Epoch 2/5\n",
            "Epoch: 2, Mi is 2.395695447921753\n",
            "Training Bob in Epoch 3/5\n",
            "Epoch: 3, Mi is 2.432936906814575\n",
            "Training Bob in Epoch 4/5\n",
            "Epoch: 4, Mi is 2.4498403072357178\n",
            "Training Bob in Epoch 5/5\n",
            "Epoch: 5, Mi is 2.4670753479003906\n",
            "Training Bob in Epoch 1/5\n",
            "Epoch: 1, Mi is 2.484712839126587\n",
            "Training Bob in Epoch 2/5\n",
            "Epoch: 2, Mi is 2.492199182510376\n",
            "Training Bob in Epoch 3/5\n",
            "Epoch: 3, Mi is 2.5072922706604004\n",
            "Training Bob in Epoch 4/5\n",
            "Epoch: 4, Mi is 2.5158066749572754\n",
            "Training Bob in Epoch 5/5\n",
            "Epoch: 5, Mi is 2.520376443862915\n",
            "Training Bob in Epoch 1/5\n",
            "Iteration: 10, Epoch: 1, Loss: -2.46878, Batch_BER: 0.83000\n",
            "Iteration: 20, Epoch: 1, Loss: -2.41989, Batch_BER: 0.71000\n",
            "Iteration: 30, Epoch: 1, Loss: -2.37373, Batch_BER: 0.72000\n",
            "Iteration: 40, Epoch: 1, Loss: -2.33021, Batch_BER: 0.66800\n",
            "Iteration: 50, Epoch: 1, Loss: -2.28926, Batch_BER: 0.55000\n",
            "Iteration: 60, Epoch: 1, Loss: -2.25069, Batch_BER: 0.44600\n",
            "Iteration: 70, Epoch: 1, Loss: -2.21415, Batch_BER: 0.44200\n",
            "Iteration: 80, Epoch: 1, Loss: -2.17946, Batch_BER: 0.38200\n",
            "Iteration: 90, Epoch: 1, Loss: -2.14645, Batch_BER: 0.34200\n",
            "Iteration: 100, Epoch: 1, Loss: -2.11499, Batch_BER: 0.32800\n",
            "Interim result for Epoch: 1, Loss: -2.11499, Batch_BER: 0.32800\n",
            "Training Bob in Epoch 2/5\n",
            "Iteration: 10, Epoch: 2, Loss: 1.21367, Batch_BER: 0.25400\n",
            "Iteration: 20, Epoch: 2, Loss: 1.17334, Batch_BER: 0.20000\n",
            "Iteration: 30, Epoch: 2, Loss: 1.13364, Batch_BER: 0.14200\n",
            "Iteration: 40, Epoch: 2, Loss: 1.09651, Batch_BER: 0.12000\n",
            "Iteration: 50, Epoch: 2, Loss: 1.05990, Batch_BER: 0.12200\n",
            "Iteration: 60, Epoch: 2, Loss: 1.02595, Batch_BER: 0.08400\n",
            "Iteration: 70, Epoch: 2, Loss: 0.99404, Batch_BER: 0.08200\n",
            "Iteration: 80, Epoch: 2, Loss: 0.96345, Batch_BER: 0.06800\n",
            "Iteration: 90, Epoch: 2, Loss: 0.93452, Batch_BER: 0.07800\n",
            "Iteration: 100, Epoch: 2, Loss: 0.90733, Batch_BER: 0.06600\n",
            "Interim result for Epoch: 2, Loss: 0.90733, Batch_BER: 0.06600\n",
            "Training Bob in Epoch 3/5\n",
            "Iteration: 10, Epoch: 3, Loss: 0.62476, Batch_BER: 0.09800\n",
            "Iteration: 20, Epoch: 3, Loss: 0.60639, Batch_BER: 0.07600\n",
            "Iteration: 30, Epoch: 3, Loss: 0.58934, Batch_BER: 0.06600\n",
            "Iteration: 40, Epoch: 3, Loss: 0.57398, Batch_BER: 0.07600\n",
            "Iteration: 50, Epoch: 3, Loss: 0.56019, Batch_BER: 0.07400\n",
            "Iteration: 60, Epoch: 3, Loss: 0.54674, Batch_BER: 0.06600\n",
            "Iteration: 70, Epoch: 3, Loss: 0.53413, Batch_BER: 0.05600\n",
            "Iteration: 80, Epoch: 3, Loss: 0.52262, Batch_BER: 0.06200\n",
            "Iteration: 90, Epoch: 3, Loss: 0.51173, Batch_BER: 0.07800\n",
            "Iteration: 100, Epoch: 3, Loss: 0.50154, Batch_BER: 0.06400\n",
            "Interim result for Epoch: 3, Loss: 0.50154, Batch_BER: 0.06400\n",
            "Training Bob in Epoch 4/5\n",
            "Iteration: 10, Epoch: 4, Loss: 0.39554, Batch_BER: 0.07200\n",
            "Iteration: 20, Epoch: 4, Loss: 0.38699, Batch_BER: 0.05800\n",
            "Iteration: 30, Epoch: 4, Loss: 0.38150, Batch_BER: 0.07200\n",
            "Iteration: 40, Epoch: 4, Loss: 0.37330, Batch_BER: 0.05400\n",
            "Iteration: 50, Epoch: 4, Loss: 0.36632, Batch_BER: 0.04400\n",
            "Iteration: 60, Epoch: 4, Loss: 0.36171, Batch_BER: 0.06800\n",
            "Iteration: 70, Epoch: 4, Loss: 0.35594, Batch_BER: 0.06600\n",
            "Iteration: 80, Epoch: 4, Loss: 0.35152, Batch_BER: 0.06000\n",
            "Iteration: 90, Epoch: 4, Loss: 0.34711, Batch_BER: 0.08200\n",
            "Iteration: 100, Epoch: 4, Loss: 0.34274, Batch_BER: 0.06000\n",
            "Interim result for Epoch: 4, Loss: 0.34274, Batch_BER: 0.06000\n",
            "Training Bob in Epoch 5/5\n",
            "Iteration: 10, Epoch: 5, Loss: 0.29578, Batch_BER: 0.06000\n",
            "Iteration: 20, Epoch: 5, Loss: 0.29682, Batch_BER: 0.06800\n",
            "Iteration: 30, Epoch: 5, Loss: 0.29502, Batch_BER: 0.07800\n",
            "Iteration: 40, Epoch: 5, Loss: 0.28794, Batch_BER: 0.04800\n",
            "Iteration: 50, Epoch: 5, Loss: 0.28480, Batch_BER: 0.05800\n",
            "Iteration: 60, Epoch: 5, Loss: 0.28215, Batch_BER: 0.07200\n",
            "Iteration: 70, Epoch: 5, Loss: 0.27772, Batch_BER: 0.04800\n",
            "Iteration: 80, Epoch: 5, Loss: 0.27438, Batch_BER: 0.05000\n",
            "Iteration: 90, Epoch: 5, Loss: 0.27233, Batch_BER: 0.05600\n",
            "Iteration: 100, Epoch: 5, Loss: 0.26947, Batch_BER: 0.05400\n",
            "Interim result for Epoch: 5, Loss: 0.26947, Batch_BER: 0.05400\n",
            "Time for the training is 63.328749895095825 sec,\n",
            "CPU times: user 1min 1s, sys: 8.56 s, total: 1min 9s\n",
            "Wall time: 1min 3s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "D5B2TUanPC5d",
        "colab_type": "code",
        "outputId": "cabe345a-5190-4f2e-e0fb-52aa9d8833b5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 295
        }
      },
      "source": [
        "gan_encoder.trainable = False\n",
        "gan_decoder.trainable = False\n",
        "\n",
        "test_encoding(M,n)   \n"
      ],
      "execution_count": 29,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nRU5ErkJggg==\n",
            "text/plain": [
              "<Figure size 288x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ukO76l6yIoPc",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# test msg sequence for normal encoding\n",
        "N_test = 500000\n",
        "test_msg = np.random.randint(M, size=N_test)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9OU_MJZoZEfp",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#gan_encoder.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IDL49c-WHZhQ",
        "colab_type": "text"
      },
      "source": [
        "Wasserstein GAN training"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "n4gYXZf9hkzY",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "@tf.function\n",
        "def w_train():\n",
        "  gen_gradients, disc_gradients = compute_gradients()\n",
        "  apply_gradients(gen_gradients, disc_gradients)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "PPsFbJwchvhq",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def compute_gradients():\n",
        "  \"\"\" passes through the network and computes loss\n",
        "  \"\"\"\n",
        "  x= tf.random.normal((batch_size,n),dtype=tf.dtypes.float32)\n",
        "  x =x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  real_data = tf.concat(values=[real_channel(x,noise_std), x], axis=1)\n",
        "  with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n",
        "    disc_loss, gen_loss = compute_loss(real_data)\n",
        "  gen_gradients = gen_tape.gradient(gen_loss, w_generator.trainable_variables)\n",
        "  disc_gradients = disc_tape.gradient(disc_loss, w_discriminator.trainable_variables)\n",
        "\n",
        "  #print(\"compute_gradients\")\n",
        "\n",
        "  return gen_gradients, disc_gradients"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2019-05-14T06:31:38.068468Z",
          "start_time": "2019-05-14T06:31:38.046751Z"
        },
        "colab_type": "code",
        "id": "Wyipg-4oSYb1",
        "colab": {}
      },
      "source": [
        "def compute_loss(real_data):\n",
        "  \"\"\" passes through the network and computes loss\n",
        "  \"\"\"\n",
        "        ### pass through network\n",
        "        # generating noise from a uniform distribution\n",
        "  ####Mein noise ist anders als hier\n",
        "  gradient_penalty_weight = 0.1   #0.5\n",
        "  x= tf.random.normal((batch_size,n),dtype=tf.dtypes.float32)\n",
        "  x_samp =x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  #print(x_samp)\n",
        "  # run noise through generator\n",
        "  x_gen = tf.concat(values=[w_generator(x_samp),x_samp], axis=1)     # x_gen zu fake_data\n",
        "  # discriminate x and x_gen\n",
        "  logits_x = w_discriminator(real_data)\n",
        "  logits_x_gen = w_discriminator(x_gen)\n",
        "\n",
        "  # gradient penalty\n",
        "  d_regularizer = gradient_penalty(real_data, x_gen)\n",
        "        ### losses\n",
        "  disc_loss = (tf.reduce_mean(logits_x) - tf.reduce_mean(logits_x_gen)+ d_regularizer * gradient_penalty_weight)\n",
        "\n",
        "        # losses of fake with label \"1\"\n",
        "  gen_loss = tf.reduce_mean(logits_x_gen)\n",
        "  return disc_loss, gen_loss\n",
        "\n",
        "\n",
        "\n",
        "def apply_gradients(gen_gradients, disc_gradients):\n",
        "  w_gen_optimizer.apply_gradients(zip(gen_gradients, w_generator.trainable_variables))\n",
        "  w_disc_optimizer.apply_gradients(zip(disc_gradients, w_discriminator.trainable_variables))\n",
        "\n",
        "def gradient_penalty(x, x_gen):\n",
        "  epsilon = tf.random.uniform([x.shape[0], 1, 1, 1], 0.0, 1.0)\n",
        "  x_hat = epsilon * x + (1 - epsilon) * x_gen\n",
        "  with tf.GradientTape() as t:\n",
        "      t.watch(x_hat)\n",
        "      d_hat = w_discriminator(x_hat)\n",
        "  gradients = t.gradient(d_hat, x_hat)\n",
        "  ddx = tf.sqrt(tf.reduce_sum(gradients ** 2, axis=[1, 2]))\n",
        "  d_regularizer = tf.reduce_mean((ddx - 1.0) ** 2)\n",
        "  #print(\"gradient_penalty\")\n",
        "  return d_regularizer\n",
        "\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2019-05-14T06:31:39.047233Z",
          "start_time": "2019-05-14T06:31:38.222179Z"
        },
        "colab_type": "code",
        "id": "dSYjNRAwSYb7",
        "colab": {}
      },
      "source": [
        "# optimizers\n",
        "w_gen_optimizer = tf.keras.optimizers.Adam(0.0001, beta_1=0.5)#,beta_2 =0.1)  #RMSprop(0.0001) works as well\n",
        "w_disc_optimizer = tf.keras.optimizers.Adam(0.0001, beta_1=0.5)#,beta_2 =0.1)  #(0.001)# train the model\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2019-05-14T06:31:39.152670Z",
          "start_time": "2019-05-14T06:31:39.058505Z"
        },
        "colab_type": "code",
        "id": "pKkEX9yBSYcB",
        "colab": {}
      },
      "source": [
        "# a pandas dataframe to save the loss information to\n",
        "losses = pd.DataFrame(columns = ['disc_loss', 'gen_loss'])\n",
        "time_to_train_w_gan = 0"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2019-05-14T07:04:26.791634Z",
          "start_time": "2019-05-14T07:04:17.126436Z"
        },
        "colab_type": "code",
        "id": "00dI2M4iSYcE",
        "outputId": "c9e9ae97-65fc-4ea7-8d16-4937ac24e4ef",
        "cellView": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 660
        }
      },
      "source": [
        "%%time\n",
        "batch_size = 100\n",
        "n_epochs = 2501   #2501\n",
        "start = time.time()\n",
        "for epoch in range(n_epochs):\n",
        "  x= tf.random.normal((batch_size,n),dtype=tf.dtypes.float32)\n",
        "  x_samp =x/tf.sqrt(2*tf.reduce_mean(tf.square(x)))\n",
        "  w_train()\n",
        "    # test on holdout\n",
        "  loss = []\n",
        "  if epoch%500 == 0:\n",
        "    real_c = tf.concat(values=[real_channel(x,noise_std), x], axis=1)\n",
        "    fake_c = w_generator(x)\n",
        "    #real_eval_data, fake_eval_data, inputs = get_evaluation_data()\n",
        "    #test_eval(real_eval_data, fake_eval_data, inputs)\n",
        "    tf.print(fake_c[0])\n",
        "    #tf.print(disc_loss, gen_loss)\n",
        "  real_data = tf.concat(values=[real_channel(x,noise_std), x], axis=1)  \n",
        "  loss.append(compute_loss(real_data))\n",
        "  losses.loc[len(losses)] = np.mean(loss, axis=0)\n",
        "  if epoch%100 == 0:\n",
        "    print(\n",
        "       \"Epoch: {} | disc_loss: {} | gen_loss: {}\".format(\n",
        "            epoch, losses.disc_loss.values[-1], losses.gen_loss.values[-1]\n",
        "        )  )\n",
        "time_to_train_w_gan += time.time()-start\n",
        "print()\n",
        "tf.saved_model.save(w_generator,'/tmp/saved_model/')"
      ],
      "execution_count": 37,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[0.16060935 0.00944230705]\n",
            "Epoch: 0 | disc_loss: 0.0766509398818016 | gen_loss: 0.49864423274993896\n",
            "Epoch: 100 | disc_loss: 0.046292178332805634 | gen_loss: 0.4994540512561798\n",
            "Epoch: 200 | disc_loss: 0.04191121459007263 | gen_loss: 0.4906556308269501\n",
            "Epoch: 300 | disc_loss: 0.03165322169661522 | gen_loss: 0.4983702003955841\n",
            "Epoch: 400 | disc_loss: 0.01042231172323227 | gen_loss: 0.5218945145606995\n",
            "[-0.601687372 -0.156715766]\n",
            "Epoch: 500 | disc_loss: -0.0313902422785759 | gen_loss: 0.5302461385726929\n",
            "Epoch: 600 | disc_loss: 0.02352713793516159 | gen_loss: 0.45662766695022583\n",
            "Epoch: 700 | disc_loss: 0.03307569772005081 | gen_loss: 0.46432504057884216\n",
            "Epoch: 800 | disc_loss: -0.037785604596138 | gen_loss: 0.5276745557785034\n",
            "Epoch: 900 | disc_loss: -0.09916341304779053 | gen_loss: 0.5586404204368591\n",
            "[0.646110415 0.0420811251]\n",
            "Epoch: 1000 | disc_loss: -0.07401721924543381 | gen_loss: 0.5244244933128357\n",
            "Epoch: 1100 | disc_loss: -0.017699770629405975 | gen_loss: 0.4712587296962738\n",
            "Epoch: 1200 | disc_loss: 0.03150538355112076 | gen_loss: 0.4364427328109741\n",
            "Epoch: 1300 | disc_loss: -0.04004456102848053 | gen_loss: 0.5011851191520691\n",
            "Epoch: 1400 | disc_loss: -0.06978889554738998 | gen_loss: 0.5182616710662842\n",
            "[-0.593861938 0.677673757]\n",
            "Epoch: 1500 | disc_loss: -0.043571989983320236 | gen_loss: 0.4871896803379059\n",
            "Epoch: 1600 | disc_loss: -0.024378042668104172 | gen_loss: 0.4611610174179077\n",
            "Epoch: 1700 | disc_loss: -0.010624262504279613 | gen_loss: 0.44587039947509766\n",
            "Epoch: 1800 | disc_loss: -0.0192568376660347 | gen_loss: 0.445831835269928\n",
            "Epoch: 1900 | disc_loss: -0.005190467461943626 | gen_loss: 0.4420323073863983\n",
            "[0.283454359 0.125772208]\n",
            "Epoch: 2000 | disc_loss: -0.013979166746139526 | gen_loss: 0.44982588291168213\n",
            "Epoch: 2100 | disc_loss: -0.019476518034934998 | gen_loss: 0.45810118317604065\n",
            "Epoch: 2200 | disc_loss: 0.0177441518753767 | gen_loss: 0.43937844038009644\n",
            "Epoch: 2300 | disc_loss: 0.010877931490540504 | gen_loss: 0.45312899351119995\n",
            "Epoch: 2400 | disc_loss: -0.00055735744535923 | gen_loss: 0.46861502528190613\n",
            "[-0.220054165 -0.105061844]\n",
            "Epoch: 2500 | disc_loss: -0.0050665587186813354 | gen_loss: 0.4629852771759033\n",
            "\n",
            "INFO:tensorflow:Assets written to: /tmp/saved_model/assets\n",
            "CPU times: user 1min 27s, sys: 6.52 s, total: 1min 33s\n",
            "Wall time: 1min 13s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "PB4rw6Qhtdbe",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "w_generator.trainable =False"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "outputId": "d824fdb1-327e-400c-9e2e-3af4b7942a51",
        "id": "RpXE1rEFJSwC",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 537
        }
      },
      "source": [
        "\n",
        "%%time\n",
        "w_gan_decoder = get_gan_decoder(M)\n",
        "w_gan_encoder = get_gan_encoder(M)\n",
        "\n",
        "gan_AE = tf.keras.models.Sequential([w_gan_encoder,w_generator,w_gan_decoder])\n",
        "data, test_data = random_sample(10000000), random_sample(10000)\n",
        "start = time.time()\n",
        "gan_AE.compile(optimizer=tf.keras.optimizers.Nadam(lr=0.005),loss='sparse_categorical_crossentropy',metrics=['accuracy'])\n",
        "history = gan_AE.fit(data, data, batch_size=500,steps_per_epoch=400, epochs=10)\n",
        "#time_to_train_gan += time.time()-start\n",
        "time_to_train_w_gan += time.time()-start\n",
        "tf.print ('Time for the training is {} sec,'.format(time_to_train_w_gan))\n",
        "gan_AE.summary()  "
      ],
      "execution_count": 39,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Train on 10000000 samples\n",
            "Epoch 1/10\n",
            "  200000/10000000 [..............................] - ETA: 3:01 - loss: 0.8386 - accuracy: 0.8281Epoch 2/10\n",
            "  197500/10000000 [..............................] - ETA: 1:14 - loss: 0.0623 - accuracy: 1.0000Epoch 3/10\n",
            "  198500/10000000 [..............................] - ETA: 1:14 - loss: 0.0169 - accuracy: 1.0000Epoch 4/10\n",
            "  196500/10000000 [..............................] - ETA: 1:15 - loss: 0.0078 - accuracy: 1.0000Epoch 5/10\n",
            "  196000/10000000 [..............................] - ETA: 1:12 - loss: 0.0045 - accuracy: 1.0000Epoch 6/10\n",
            "  200000/10000000 [..............................] - ETA: 1:16 - loss: 0.0028 - accuracy: 1.0000Epoch 7/10\n",
            "  195500/10000000 [..............................] - ETA: 1:12 - loss: 0.0019 - accuracy: 1.0000Epoch 8/10\n",
            "  196500/10000000 [..............................] - ETA: 1:12 - loss: 0.0014 - accuracy: 1.0000Epoch 9/10\n",
            "  195000/10000000 [..............................] - ETA: 1:12 - loss: 9.8144e-04 - accuracy: 1.0000Epoch 10/10\n",
            "  195500/10000000 [..............................] - ETA: 1:13 - loss: 7.3421e-04 - accuracy: 1.0000Time for the training is 89.9627013206482 sec,\n",
            "Model: \"sequential_12\"\n",
            "_________________________________________________________________\n",
            "Layer (type)                 Output Shape              Param #   \n",
            "=================================================================\n",
            "sequential_11 (Sequential)   (None, None)              562       \n",
            "_________________________________________________________________\n",
            "model (Model)                (None, 2)                 1294      \n",
            "_________________________________________________________________\n",
            "sequential_10 (Sequential)   (None, 16)                320       \n",
            "=================================================================\n",
            "Total params: 2,176\n",
            "Trainable params: 882\n",
            "Non-trainable params: 1,294\n",
            "_________________________________________________________________\n",
            "CPU times: user 22.6 s, sys: 626 ms, total: 23.2 s\n",
            "Wall time: 17.6 s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "outputId": "ed69d6af-a242-4274-94cc-ab03cccf9ae0",
        "id": "1FqJP20JLu-j",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 295
        }
      },
      "source": [
        "w_gan_encoder.trainable = False\n",
        "w_gan_decoder.trainable = False\n",
        "\n",
        "test_w_encoding(M=M,n=n)   \n"
      ],
      "execution_count": 40,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 288x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PK5wA-zzHScv",
        "colab_type": "text"
      },
      "source": [
        "### Comparison"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "outputId": "952fef16-8ce6-4611-9bfe-90e4e38db86d",
        "id": "RzC-yCM_wKW9",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 106
        }
      },
      "source": [
        "mi_bber_data = mi_Test_AE(test_msg)"
      ],
      "execution_count": 41,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Progress: 6 of 30 parts\n",
            "Progress: 12 of 30 parts\n",
            "Progress: 18 of 30 parts\n",
            "Progress: 24 of 30 parts\n",
            "Progress: 30 of 30 parts\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7M-S0sbhIoPw",
        "colab_type": "code",
        "outputId": "bb502f9d-8773-40b7-c165-ee69fb12820b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 106
        }
      },
      "source": [
        "gan_bber_data = gan_Test_AE(test_msg)"
      ],
      "execution_count": 42,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Progress: 6 of 30 parts\n",
            "Progress: 12 of 30 parts\n",
            "Progress: 18 of 30 parts\n",
            "Progress: 24 of 30 parts\n",
            "Progress: 30 of 30 parts\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9f4bW0QpJkhb",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 106
        },
        "outputId": "f4935725-40ee-4356-c80e-37b4499cae43"
      },
      "source": [
        "w_gan_bber_data = w_gan_Test_AE(test_msg)"
      ],
      "execution_count": 43,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Progress: 6 of 30 parts\n",
            "Progress: 12 of 30 parts\n",
            "Progress: 18 of 30 parts\n",
            "Progress: 24 of 30 parts\n",
            "Progress: 30 of 30 parts\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "UYdEm0eQIoP2",
        "colab_type": "code",
        "outputId": "80eebbb1-4bc5-4932-f35a-45e0f5cf9601",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 406
        }
      },
      "source": [
        "# Approximate 16 QAM Error\n",
        "def SIXT_QAM_sim(ebno):\n",
        "    return (3.0/2)*special.erfc(np.sqrt((4.0/10)*10.**(ebno/10)))\n",
        "\n",
        "ebnodbs = np.linspace(0,15,16)\n",
        "fig = plt.figure(figsize=(8, 5))\n",
        "plt.semilogy(mi_bber_data[0], mi_bber_data[1], 'o-')\n",
        "plt.semilogy(gan_bber_data[0], gan_bber_data[1], '^-')\n",
        "plt.semilogy(w_gan_bber_data[0], w_gan_bber_data[1], '+-')\n",
        "plt.semilogy(ebnodbs, SIXT_QAM_sim(ebnodbs), '*-');\n",
        "plt.gca().set_ylim(1e-5, 1)\n",
        "plt.gca().set_xlim(0, 15)\n",
        "plt.ylabel(\"Batch Symbol Error Rate\", fontsize=14, rotation=90)\n",
        "plt.xlabel(\"SNR [dB]\", fontsize=18)\n",
        "plt.legend(['AE with MINE','AE with GAN','AE with WGAN', '16QAM'],\n",
        "           prop={'size': 14}, loc='upper right');\n",
        "plt.grid(True, which=\"both\")\n",
        "\n",
        "print('time to train the AE Model with MI',time_to_train_mi)\n",
        "print('time to train the AE Model with GAN',time_to_train_gan)\n",
        "tf.print ('time to train the AE Model with WGAN,',time_to_train_w_gan)"
      ],
      "execution_count": 44,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "time to train the AE Model with MI 63.32874536514282\n",
            "time to train the AE Model with GAN 31.365469455718994\n",
            "time to train the AE Model with WGAN, 89.9627013206482\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nRU5ErkJggg==\n",
            "text/plain": [
              "<Figure size 576x360 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bBes21qLlcS8",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#generator.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "q60pg9b9Rz7C",
        "colab_type": "code",
        "outputId": "9ce67e7c-6f24-498d-8d2d-3ab9216ccf24",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 55
        }
      },
      "source": [
        "print(gan_bber_data[1])"
      ],
      "execution_count": 46,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[<tf.Tensor: id=1167997, shape=(), dtype=float32, numpy=0.474934>, <tf.Tensor: id=1168049, shape=(), dtype=float32, numpy=0.443874>, <tf.Tensor: id=1168101, shape=(), dtype=float32, numpy=0.413276>, <tf.Tensor: id=1168153, shape=(), dtype=float32, numpy=0.38206>, <tf.Tensor: id=1168205, shape=(), dtype=float32, numpy=0.34831>, <tf.Tensor: id=1168257, shape=(), dtype=float32, numpy=0.317248>, <tf.Tensor: id=1168309, shape=(), dtype=float32, numpy=0.286014>, <tf.Tensor: id=1168361, shape=(), dtype=float32, numpy=0.25317>, <tf.Tensor: id=1168413, shape=(), dtype=float32, numpy=0.224596>, <tf.Tensor: id=1168465, shape=(), dtype=float32, numpy=0.196254>, <tf.Tensor: id=1168517, shape=(), dtype=float32, numpy=0.167636>, <tf.Tensor: id=1168569, shape=(), dtype=float32, numpy=0.141908>, <tf.Tensor: id=1168621, shape=(), dtype=float32, numpy=0.119074>, <tf.Tensor: id=1168673, shape=(), dtype=float32, numpy=0.098306>, <tf.Tensor: id=1168725, shape=(), dtype=float32, numpy=0.079698>, <tf.Tensor: id=1168777, shape=(), dtype=float32, numpy=0.063784>, <tf.Tensor: id=1168829, shape=(), dtype=float32, numpy=0.050424>, <tf.Tensor: id=1168881, shape=(), dtype=float32, numpy=0.038736>, <tf.Tensor: id=1168933, shape=(), dtype=float32, numpy=0.029508>, <tf.Tensor: id=1168985, shape=(), dtype=float32, numpy=0.022166>, <tf.Tensor: id=1169037, shape=(), dtype=float32, numpy=0.015898>, <tf.Tensor: id=1169089, shape=(), dtype=float32, numpy=0.011612>, <tf.Tensor: id=1169141, shape=(), dtype=float32, numpy=0.008204>, <tf.Tensor: id=1169193, shape=(), dtype=float32, numpy=0.005362>, <tf.Tensor: id=1169245, shape=(), dtype=float32, numpy=0.003706>, <tf.Tensor: id=1169297, shape=(), dtype=float32, numpy=0.00245>, <tf.Tensor: id=1169349, shape=(), dtype=float32, numpy=0.00159>, <tf.Tensor: id=1169401, shape=(), dtype=float32, numpy=0.000956>, <tf.Tensor: id=1169453, shape=(), dtype=float32, numpy=0.00055>, <tf.Tensor: id=1169505, shape=(), dtype=float32, numpy=0.00028>, <tf.Tensor: id=1169557, shape=(), dtype=float32, numpy=0.00016>]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "RJl0KB-5at3r",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}